/*
 * This file is part of the OSMembrane project.
 * More informations under www.osmembrane.de
 * 
 * The project is licensed under the GNU GENERAL PUBLIC LICENSE 3.0.
 * for more details about the license see http://www.osmembrane.de/license/
 * 
 * Source: $HeadURL: http://osmembrane-gui.googlecode.com/svn/sources/src/de/osmembrane/tools/PipelineExecutor.java $ ($Revision: 839 $)
 * Last changed: $Date: 2011-02-17 12:17:58 +0000 (Thu, 17 Feb 2011) $
 */



package de.osmembrane.tools;

import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import javax.swing.Action;

import de.osmembrane.Application;
import de.osmembrane.controller.ActionRegistry;
import de.osmembrane.exceptions.ControlledException;
import de.osmembrane.exceptions.ExceptionSeverity;
import de.osmembrane.view.interfaces.IExecutionStateDialog;

/**
 * Executor for a pipeline
 * 
 * @author jakob_jarosch
 */
public class PipelineExecutor extends Thread implements WindowListener {

	private String osmosisPath;
	private File workingDirectory;
	private List<String> parameters;
	private IExecutionStateDialog dialog;

	private Class<? extends Action> callbackEvent;

	private int executionState = -1;
	
	/**
	 * Initialize the pipeline executor.
	 * 
	 * @throws IllegalArgumentException
	 *             when the osmosis executable is not runnable.
	 */
	public PipelineExecutor(String osmosisPath, String workingDirectory,
			List<String> parameters, IExecutionStateDialog dialog) {
		setPath(osmosisPath);
		this.workingDirectory = new File(workingDirectory);
		this.parameters = parameters;
		this.dialog = dialog;
		dialog.addWindowListener(this);

		dialog.setState(I18N.getInstance().getString(
				"Tools.PipelineExecutor.ParametersSet"));
		dialog.setProgress(5);
		dialog.setCloseButtonCaption(I18N.getInstance()
				.getString("View.Cancel"));
	}

	/**
	 * Sets a call back action which is called after completing of the execution.
	 * @param event
	 */
	public void setCallbackAction(Class<? extends Action> event) {
		this.callbackEvent = event;
	}

	@Override
	public void run() {
		dialog.setState(I18N.getInstance().getString(
				"Tools.PipelineExecutor.StartingOsmosis"));
		dialog.setProgress(10);

		List<String> cmdLine = new ArrayList<String>(parameters.size() + 1);
		cmdLine.add(osmosisPath);
		cmdLine.addAll(parameters);

		try {
			ProcessBuilder processBuilder = new ProcessBuilder(cmdLine);
			Process process = processBuilder.directory(workingDirectory)
					.redirectErrorStream(true).start();

			final BufferedReader reader = new BufferedReader(
					new InputStreamReader(process.getInputStream()));

			dialog.setState(I18N.getInstance().getString(
					"Tools.PipelineExecutor.RunningOsmosis"));
			dialog.setProgress(10);

			String line = null;
			do {
				try {
					ReadThread thread = new ReadThread(reader);
					thread.start();
					thread.join();
					line = thread.getLine();
				} catch (InterruptedException e) {
					interrupt();
					line = null;
				}

				if (line != null) {
					dialog.addOutputLine(line);
				}
			} while (line != null && !isInterrupted());

			if (!isInterrupted()) {
				int exitValue = -255;
				try {
					exitValue = process.waitFor();
					if (exitValue == 0) {
						dialog.setState(I18N.getInstance().getString(
								"Tools.PipelineExecutor.Finished"));
						dialog.setCloseButtonCaption(I18N.getInstance()
								.getString("View.OK"));
						dialog.setProgress(100);
					} else {
						dialog.setState(I18N.getInstance().getString(
								"Tools.PipelineExecutor.Failed"));
						dialog.setCloseButtonCaption(I18N.getInstance()
								.getString("View.Close"));
					}

					executionState = exitValue;
				} catch (InterruptedException e) {
					interrupt();
				}
			} else {
				process.destroy();
				reader.close();
			}

		} catch (IOException e) {
			dialog.setState(I18N.getInstance().getString(
					"Tools.PipelineExecutor.IOExecption"));
			dialog.setProgress(0);
			Application.handleException(new ControlledException(this,
					ExceptionSeverity.WARNING, e, I18N.getInstance().getString(
							"Tools.PipelineExecutor.IOExecption")));
		}
	}

	/**
	 * Sets the osmosisPath and validates it.
	 * 
	 * @param osmosisPath
	 */
	private void setPath(String osmosisPath) {
		if (osmosisPath == null) {
			throw new IllegalArgumentException();
		}
		File f = new File(osmosisPath);
		if (!f.isFile() || !f.canExecute() || !f.canRead()) {
			throw new IllegalArgumentException();
		}
		this.osmosisPath = osmosisPath;
	}

	/**
	 * The Windows-closed action of the dialog in the view.
	 */
	@Override
	public void windowClosing(WindowEvent e) {
		dialog.removeWindowListener(this);

		if (this.isAlive()) {
			this.interrupt();
		} else {
			if (callbackEvent != null && executionState == 0) {
				ActionRegistry.getInstance().get(callbackEvent)
						.actionPerformed(new ActionEvent(this, 0, "finished"));
			}
		}
	}

	@Override
	public void windowClosed(WindowEvent e) {
	}

	@Override
	public void windowOpened(WindowEvent e) {
	}

	@Override
	public void windowIconified(WindowEvent e) {
	}

	@Override
	public void windowDeiconified(WindowEvent e) {
	}

	@Override
	public void windowActivated(WindowEvent e) {
	}

	@Override
	public void windowDeactivated(WindowEvent e) {
	}
}

/**
 * ReadThread which is able to be interrupted while reading a line.
 * 
 * @author jakob_jarosch
 */
class ReadThread extends Thread {

	private BufferedReader reader;
	private String line;

	public ReadThread(BufferedReader reader) {
		this.reader = reader;
	}

	@Override
	public void run() {
		line = null;
		try {
			line = reader.readLine();
		} catch (IOException e) {
			line = null;
		}
	}

	public String getLine() {
		return line;
	}
}
